///
/// Classes for n-dimensional vectors
///
#pragma once

namespace Math
{
    ///
    /// A vector containing two values of type Scalar
    ///
    template <typename Scalar>
    class Vector2
    {
    // Structors
    public:
        Vector2() : x(0), y(0) {}
        Vector2(Scalar x, Scalar y) : x(x), y(y) {}
        
    // Overloaded operators
    public:
        friend Vector2<Scalar> operator +(const Vector2<Scalar>& vec1, const Vector2<Scalar>& vec2)
            { return Vector2<Scalar>(vec1.x+vec2.x, vec1.y+vec2.y); }

		friend Vector2<Scalar> operator +( const Scalar& scalar, const Vector2<Scalar>& vec1)
            { return Vector2<Scalar>(vec1.x+scalar, vec1.y+scalar); }

		friend Vector2<Scalar> operator +(const Vector2<Scalar>& vec1, const Scalar& scalar)
            { return Vector2<Scalar>(vec1.x+scalar, vec1.y+scalar); }

        friend Vector2<Scalar> operator -(const Vector2<Scalar>& vec1, const Vector2<Scalar>& vec2)
            { return Vector2<Scalar>(vec1.x-vec2.x, vec1.y-vec2.y); }

		friend Vector2<Scalar> operator -(const Vector2<Scalar>& vec1, const Scalar& scalar)
            { return Vector2<Scalar>(vec1.x-scalar, vec1.y-scalar); }
		
		friend Vector2<Scalar> operator -(const Vector2<Scalar>& vec)
		    { return Vector2<Scalar>(-vec.x, -vec.y); }
		
		friend bool operator ==(const Vector2<Scalar>& vec1, const Vector2<Scalar>& vec2)
		    { return vec1.x == vec2.x && vec1.y == vec2.y; }
		    
		friend Vector2<Scalar> operator *(const Vector2<Scalar>& vec, const Scalar& scalar)
		    { return Vector2<Scalar>(vec.x * scalar, vec.y * scalar); }

		friend Vector2<Scalar> operator *(const Scalar& scalar, const Vector2<Scalar>& vec)
		    { return Vector2<Scalar>(vec.x * scalar, vec.y * scalar); }

		friend Vector2<Scalar> operator /(const Vector2<Scalar>& vec, const Scalar& scalar)
		    { return Vector2<Scalar>(vec.x / scalar, vec.y / scalar); }

    public:
        Scalar Mag() const
            { return sqrt(x*x  + y*y); }
            
        Vector2<Scalar> Rotate(double relangle, const Vector2<Scalar>& origin) const
        {
            double angle = Angle(origin, *this) + relangle;
            double dist = Distance(origin, *this );
            return Vector2d(cos(angle) * dist, sin(angle) * dist) + origin;	
        }
				
    // Members
    public:
        Scalar x, y;
    };

    ///
    /// A vector containing three values of type Scalar
    ///
    template <typename Scalar>
    class Vector3 : public Vector2<Scalar>
    {
    // Structors
    public:
        Vector3() : x(0), y(0), z(0) {}
        Vector3(Scalar x, Scalar y, Scalar z) : x(x), y(y), z(z) {}

    // Members
    public:
        Scalar z;
    };

    typedef Vector2<float> Vector2f;
    typedef Vector3<float> Vector3f;
    typedef Vector2<double> Vector2d;
    typedef Vector3<double> Vector3d;
    
    ///
    /// Returns the distance between two 2D vectors
    ///
    template <typename Scalar>
    inline Scalar Distance(Vector2<Scalar> a, Vector2<Scalar> b)
    {
        return sqrt((b.x - a.x) * (b.x - a.x) + (b.y - a.y) * (b.y - a.y));
    }
    
    template <typename Scalar>
    inline Scalar Angle(Vector2<Scalar> a, Vector2<Scalar> b)
    {
        return atan2(b.y - a.y, b.x - a.x);
    }
}